
`timescale 1ns/1ps 

module Top #(
    parameter int unsigned AXI_ID_WIDTH      = 8,
    parameter int unsigned AXI_ADDR_WIDTH    = 40,
    parameter int unsigned AXI_DATA_WIDTH    = 128,
    parameter int unsigned AXI_USER_WIDTH    = 2
)(
  output   wire                          axi_ar_valid,
  input    wire                          axi_ar_ready,
  output   wire   [AXI_ADDR_WIDTH-1:0]   axi_ar_addr,
  output   wire   [AXI_ID_WIDTH-1  :0]   axi_ar_id,
  output   wire   [7:0]                  axi_ar_len,
  output   wire   [2:0]                  axi_ar_size,
  output   wire   [1:0]                  axi_ar_burst,
  output   wire   [3:0]                  axi_ar_cache,
  output   wire   [2:0]                  axi_ar_prot,
  output   wire                          axi_aw_valid,
  input    wire                          axi_aw_ready,
  output   wire   [AXI_ADDR_WIDTH-1:0]   axi_aw_addr,
  output   wire   [AXI_ID_WIDTH-1  :0]   axi_aw_id,
  output   wire   [7:0]                  axi_aw_len,
  output   wire   [2:0]                  axi_aw_size,
  output   wire   [1:0]                  axi_aw_burst,
  output   wire   [3:0]                  axi_aw_cache,
  output   wire   [2:0]                  axi_aw_prot,
  output   wire   [AXI_ID_WIDTH-1  :0]   axi_w_id,
  output   wire                          axi_w_valid,
  input    wire                          axi_w_ready,
  output   wire   [AXI_DATA_WIDTH-1:0]   axi_w_data,
  output   wire   [15:0]                 axi_w_strb,
  output   wire                          axi_w_last,
  input    wire                          axi_b_valid,
  output   wire                          axi_b_ready,
  input    wire   [AXI_ID_WIDTH-1  :0]   axi_b_id,
  input    wire   [1:0]                  axi_b_resp,
  input    wire                          axi_r_valid,
  output   wire                          axi_r_ready,
  input    wire   [AXI_DATA_WIDTH-1:0]   axi_r_data,
  input    wire   [AXI_ID_WIDTH-1  :0]   axi_r_id,
  input    wire   [1:0]                  axi_r_resp,
  input    wire                          axi_r_last,
  input    wire   [7 :0]                 core_in_interrupt,
  output   reg    [63:0]                 debug_wave_dump_on,
  output   reg    [63:0]                 dbg_sim_cycles,
  input    wire                          reset,
  input    wire                          clk
);


// wire                            axi_aw_valid;
// wire                            axi_aw_ready;
// wire   [AXI_ADDR_WIDTH-1:0]     axi_aw_addr;
// wire   [AXI_ID_WIDTH-1  :0]     axi_aw_id;
// wire   [7:0]                    axi_aw_len;
// wire   [2:0]                    axi_aw_size;
// wire   [1:0]                    axi_aw_burst;
// wire   [3:0]                    axi_aw_cache;
// wire   [2:0]                    axi_aw_prot;
// wire                            axi_w_valid;
// wire                            axi_w_ready;
// wire   [AXI_ID_WIDTH-1  :0]     axi_w_id;
// wire   [AXI_DATA_WIDTH-1:0]     axi_w_data;
// wire   [15:0]                   axi_w_strb;
// wire                            axi_w_last;
// wire                            axi_b_valid;
// wire                            axi_b_ready;
// wire   [AXI_ID_WIDTH-1  :0]     axi_b_id;
// wire   [1:0]                    axi_b_resp;
// wire                            axi_ar_valid;
// wire                            axi_ar_ready;
// wire   [AXI_ADDR_WIDTH-1:0]     axi_ar_addr;
// wire   [AXI_ID_WIDTH-1  :0]     axi_ar_id;
// wire   [7:0]                    axi_ar_len;
// wire   [2:0]                    axi_ar_size;
// wire   [1:0]                    axi_ar_burst;
// wire   [3:0]                    axi_ar_cache;
// wire   [2:0]                    axi_ar_prot;
// wire                            axi_r_valid;
// wire                            axi_r_ready;
// wire   [AXI_DATA_WIDTH-1:0]     axi_r_data;
// wire   [AXI_ID_WIDTH-1  :0]     axi_r_id;
// wire   [1:0]                    axi_r_resp;
// wire                            axi_r_last;
// wire   [7:0]                    ext_interrupt;
// wire                            reset;

`ifdef SIM_DUMP
initial begin
      /// method 1: this will dump only ct_top module
      // $fsdbDumpon;
      // $fsdbDumpfile("soc_top.fsdb");
      // $fsdbDumpvars(0, soc_top);

      $dumpfile("soc_top.vcd");
      $dumpvars;  

      /// method 2: this will dump all module
      // $fsdbDumpvars();
end
`endif


//----------------------------------------------------------
//                     Module Instance
//----------------------------------------------------------
cpu_subsystem  cpu_subsystem (
  // AR
  .biu_pad_araddr        (axi_ar_addr       ),
  .biu_pad_arburst       (axi_ar_burst      ),
  .biu_pad_arcache       (axi_ar_cache      ),
  .biu_pad_arid          (axi_ar_id         ),
  .biu_pad_arlen         (axi_ar_len        ),
  .biu_pad_arlock        (axi_ar_lock       ),
  .biu_pad_arprot        (axi_ar_prot       ),
  .biu_pad_arsize        (axi_ar_size       ),
  .biu_pad_arvalid       (axi_ar_valid      ),
  .pad_biu_arready       (axi_ar_ready      ),
  
  // AW
  .biu_pad_awaddr        (axi_aw_addr       ),
  .biu_pad_awburst       (axi_aw_burst      ),
  .biu_pad_awcache       (axi_aw_cache      ),
  .biu_pad_awid          (axi_aw_id         ),
  .biu_pad_awlen         (axi_aw_len        ),
  .biu_pad_awlock        (axi_aw_lock       ),
  .biu_pad_awprot        (axi_aw_prot       ),
  .biu_pad_awsize        (axi_aw_size       ),
  .biu_pad_awvalid       (axi_aw_valid      ),
  .pad_biu_awready       (axi_aw_ready      ),
  
  // W
  .biu_pad_wdata         (axi_w_data        ),
  .biu_pad_wid           (axi_w_id          ),
  .biu_pad_wlast         (axi_w_last        ),
  .biu_pad_wstrb         (axi_w_strb        ),
  .biu_pad_wvalid        (axi_w_valid       ),
  .pad_biu_wready        (axi_w_ready       ),
  
  // B
  .biu_pad_bready        (axi_b_ready       ),
  .pad_biu_bid           (axi_b_id          ),
  .pad_biu_bresp         (axi_b_resp        ),
  .pad_biu_bvalid        (axi_b_valid       ),
  
  // R
  .biu_pad_rready        (axi_r_ready       ),
  .pad_biu_rdata         (axi_r_data        ),
  .pad_biu_rid           (axi_r_id          ),
  .pad_biu_rlast         (axi_r_last        ),
  .pad_biu_rresp         ({2'b0,axi_r_resp} ),
  .pad_biu_rvalid        (axi_r_valid       ),

  /// MISC
  .axim_clk_en           (1'b1              ),
  .biu_pad_lpmd_b        (                  ),
  .had_pad_jtg_tdo       (                  ),
  .had_pad_jtg_tdo_en    (                  ),
  .i_pad_jtg_tms         (                  ),  /// No Input
  .pad_cpu_rst_b         (~reset            ),
  .pad_yy_dft_clk_rst_b  (~reset            ),
  .pad_had_jtg_tclk      (clk               ),
  .pad_had_jtg_tdi       ('0                ),
  .pad_had_jtg_trst_b    (~reset            ),
  .pll_cpu_clk           (clk               ),
  .ext_interrupt         (core_in_interrupt ),
  .xx_intc_vld           ('0                )
);

//----------------------------------------------------------
//                Interrupt generation Logic
//----------------------------------------------------------

reg [63:0] sim_cycles;
always @(posedge clk) begin
  if(reset) begin
      sim_cycles <= 0;
  end else begin
      sim_cycles <= sim_cycles + 1'b1;
  end
end

assign dbg_sim_cycles[63:0] = sim_cycles[63:0];

//----------------------------------------------------------
//                   Retire Logic
//----------------------------------------------------------
`define CPU_CORE cpu_subsystem.core0_subsystem

`ifdef MMU_ON
`define HALT_PC  40'h1800018000
`else 
`define HALT_PC  40'h1c018000
`endif

always @(posedge clk) begin
    if ((`CPU_CORE.core0_pad_retire0_pc == `HALT_PC)
        && `CPU_CORE.core0_pad_retire0
    || (`CPU_CORE.core0_pad_retire1_pc == `HALT_PC)
        && `CPU_CORE.core0_pad_retire1
    || (`CPU_CORE.core0_pad_retire2_pc == `HALT_PC)
        && `CPU_CORE.core0_pad_retire2
        ) begin
        $finish;
    end
end


//----------------------------------------------------------
//                   Debug Dump Wave Logic
//----------------------------------------------------------
`define CPU_CP0_REGS cpu_subsystem.core0_subsystem.core_top_0.x_ct_core.x_ct_cp0_top.x_ct_cp0_regs

always @(posedge clk) begin
    if (debug_wave_dump_on[63:0] == 64'h11) begin
       $display("debug_wave_dump_on!");
    end else if (debug_wave_dump_on[63:0] == 64'hff) begin
       $display("debug_wave_dump_off!");
    end 
end



// always @(posedge clk) begin
//     debug_wave_dump_on <= `CPU_CP0_REGS.debug_wave_dump_value[63:0]; // 64'h11;
// end


`define U_DUMP_PC  40'hffffffff
always @(posedge clk) begin
    if(reset) begin
      debug_wave_dump_on <= '0;
    end
    else if ((`CPU_CORE.core0_pad_retire0_pc == `U_DUMP_PC) && `CPU_CORE.core0_pad_retire0
          || (`CPU_CORE.core0_pad_retire1_pc == `U_DUMP_PC) && `CPU_CORE.core0_pad_retire1
          || (`CPU_CORE.core0_pad_retire2_pc == `U_DUMP_PC) && `CPU_CORE.core0_pad_retire2
             ) begin
        debug_wave_dump_on <= 64'h11;
    end
    else if(|debug_wave_dump_on[63:0]) begin
        debug_wave_dump_on <= 64'h0;
    end
end







wire commit_vld;

assign commit_vld = `CPU_CORE.core0_pad_retire0 || `CPU_CORE.core0_pad_retire1 || `CPU_CORE.core0_pad_retire2;

reg start_stats_reg;
always @(posedge clk) begin
    if (reset) begin
       start_stats_reg  <= 1'b0;
    end else if(!start_stats_reg && commit_vld) begin
       start_stats_reg  <= 1'b1;
    end 
end


reg [31: 0] commit_idle_cnt;
always @(posedge clk) begin
    if (reset || commit_vld) begin
       commit_idle_cnt[31: 0] <= 32'b0;
    end else begin
       commit_idle_cnt[31: 0] <= commit_idle_cnt[31: 0] + 1'b1;
    end 
end

always @(posedge clk) begin
    if ((commit_idle_cnt[31: 0] >= 32'h800) && start_stats_reg) begin
       $display("Too long time not retire inst!");
       $finish;
    end 
end

//----------------------------------------------------------
//                   Display Restore PC
//----------------------------------------------------------
`define RESTORE_PC  40'hc01c000564

always @(posedge clk) begin
    if ((`CPU_CORE.core0_pad_retire0_pc == `RESTORE_PC)
        && `CPU_CORE.core0_pad_retire0
    || (`CPU_CORE.core0_pad_retire1_pc == `RESTORE_PC)
        && `CPU_CORE.core0_pad_retire1
    || (`CPU_CORE.core0_pad_retire2_pc == `RESTORE_PC)
        && `CPU_CORE.core0_pad_retire2
        ) begin
        $display("CheckPoint Restore Success!");
        $display("###########################");
    end
end

endmodule



